package com.yinhd.study.threadTest;

import lombok.Data;
import lombok.SneakyThrows;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * @program: study
 * @description:
 * @Author: yinhd
 * @create: 2022-07-05 10:58
 **/
public class ThreadPoolTest {
    /*
     线程池构造函数的参数？
     -corePoolSize  核心线程数
     --正常开辟线程数
     -workQueue 存储队列
     --当核心线程全部被占用，新来的任务先存储到队列中，等待被分配核心线程
     -maxPoolSize 最大线程数
     --当核心线程和队列都不足以处理新来的任务，开辟线程，不能超过但不能超过最大线程数
     -keepAliveTime
     --如果线程池当前线程数多于corePoolSize,那么多余的线程空闲时间超时keepAliveTime,就会收回该线程
     -ThreadFactory 线程工厂
     --新的线程是由ThreadFactory创建的，默认使用Executors.defaultThreadFactory(),创建出来的线程都在
     同一个线程组。拥有同样的NORM_PRIORITY优先级并且都不是守护线程。如果自己指定ThreadFactory,那么就可以
     改变线程名，线程组，优先级，是否是守护线程等；


     添加线程规则
     1.如果线程数小于核心线程数，即使其他工作线程处于空闲状态，也会创建一个新的线程来运行新任务
     2.如果线程数等于（或大于）核心线程数，但少于最大线程数，则将任务放到队列中
     3.如果队列已满，并且线程数小于最大线程数，则穿件一个新的线程运行任务
     增减线程特点：
     -是只有在队列填满时才创建多于corePoolSize的线程，所以如果使用的是无界队列（LinkedBlockingQueue）
     那么线程数永远也不会开辟新线程

     工作队列
     -SynchronousQueue 直接交接
     --任务不会特别多，简单中转，内部没有容量，使用时注意要把最大线程数要设置的大一点
     -LinkedBlockingQueue 无界队列
     --处理的速度跟不上进队列的速度 可防止流量突增 但是容易造成内存溢出 OOM异常
     -ArrayBlockingQueue 有界队列
     --设置一个队列容量，当容量满了之后去创建新的线程
     -DelayedWorkQueue 延迟队列
     --到达一定时间从队列取出

     线程池手动创建？自动创建？
     *最好手动创建，根据业务场景自己设置线程池参数
     -创建原则
     --最佳线程数为CPU核心数的 1~2 倍
     --耗时IO型（读写数据库、文件、网络读写）：最佳线程数一般会大于CPU核心输的很多倍，以Brain Goetz
     推荐的计算方法 线程数 = CPU核心数*（1+平均等待时间/平均工作时间）
     ---等待时间：IO时间
     ---工作时间：除了IO之外的耗时

    线程池关闭
    -shutdown
    --现存的任务会执行完（包括在队列中的任务），在进来的任务不会接收
    -shutdownNow
    --立即中断线程，不论是否队列中有，都不执行了。不并且返回被终止的任务
    -isShutdown
    --线程池状态返回 只要关闭了就返回关闭状态，不管队列中任务是否都执行完
    -isTerminated
    --（源）如果所有任务在关闭后都已完成，则返回 true。请注意，除非首先调用了 shutdown 或 shutdownNow，否则 isTerminated 永远不会为真。返回：如果所有任务在关闭后都已完成，则返回 true
    -awaitTerminated
    --（源）在关闭请求后阻塞，直到所有任务都完成执行，或者发生超时，或者当前线程被中断，以先发生者为准。参数： timeout – 等待的最长时间 unit – timeout 参数的时间单位 返回：如果此执行程序终止，则为 true；如果在终止前超时，则为 false 抛出：InterruptedException – 如果在等待时中断


    任务太多，如何拒绝？
    -拒绝时机
    -- 当Executor关闭时，提交新任务会被拒绝
    -- 当Executor对最大线程和工作队列容量使用有限的边界并且已经饱和
    -拒绝策略
    --AbortPolicy 拒绝并给通知提示（抛异常）
    --DiscardPolicy 拒绝但是不给通知
    --DiscardOldestPolicy 舍弃最老的任务，接收新任务
    --CallerRunsPolicy 让提交任务的线程去执行任务


    线程池组成部分
    -线程池管理器
    -工作线程
    -任务队列
    -任务接口（Task）

    线程池实现任务复习
    */

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(4);

        ExecutorService executorService2 = Executors.newSingleThreadExecutor();

        ExecutorService executorService1 = Executors.newCachedThreadPool();  //也可能导致OOM
        /**
         * 定时线程任务池
         */
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        /**
         *线程中又有线程  类似递归树
         */
        ExecutorService executorService3 = Executors.newWorkStealingPool();

        for (int i = 0; i < 1000; i++) {
            executorService.execute(new SubThread());
        }
    }
    public  static  String date(Long time){
        Date date = new Date(1000*time);
        SimpleDateFormat dateFormat =  new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String format = dateFormat.format(date);
        return format;
    }

    static class SubThread implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
            ThreadPoolTest.date(1000L);
        }
    }
}
